//========================================================//
// Common Lib                                             //
// Data classes ans interfaces                            //
// Author: Bezdolnyj Alexey aka BAM  bamalex@ngs.ru       //
// NSU FIT May-July 2004                                  //
//========================================================//

//Linux porting by Kozlov Dmitry aka Landfox
//July 2004
//#define _BAM_DEBUG

#ifndef __PACKET_CLASSES__
#define __PACKET_CLASSES__

//#include "C:\\Documents and Settings\\sfaktorovich\\My Documents\\remoteconsole2\\vld.h"

#ifdef _BAM_DEBUG
#include <iostream>
#endif

#include <string.h>
#include "exception.h"

namespace BAM
{
	using namespace std;

#ifndef _WIN32
	typedef unsigned long long qword;
#else
	typedef unsigned __int64 qword;
#endif

	typedef unsigned char byte;
	typedef char sbyte;
	typedef unsigned short int word;
	typedef short int sword;
	typedef unsigned int dword;
	typedef int sdword;

	class PacketException : public cException
	{
	public:
		PacketException(const char *Msg)
			:cException(Msg)
		{}
	};

	static const PacketException EXC_BadMem("Bad memory operation");
	static const PacketException EXC_Unknown("Unknown exception");

	// packet data abstraction
	class cPacketData
	{
	private:
		dword vLocks;
		byte* vData;
		dword vSize, vLength;
		bool vOwn;

		void resize(dword nSize, bool fromEnd = false);

	public:
		cPacketData();
		cPacketData(const cPacketData &Pd);
		cPacketData(dword Size);
		cPacketData(const byte *Buf, dword Len);
		cPacketData(byte *Buf, dword Len, bool Own);
		~cPacketData();

		bool operator == (const cPacketData &Pd);

		dword lock();
		dword unlock();
		dword usage() const;
		void insertHeader(const byte *Buf, dword Len);
		void insertTail(const byte *Buf, dword Len);
		void removeHeader(dword Len);
		void removeTail(dword Len)
		{
			setLength(vLength - Len);
		}
		dword setRealSize(dword nSize);
		dword getRealSize() const
		{
			return vSize;
		}
		dword setLength(dword nLen);
		dword getLength() const
		{
			return vLength;
		}
		const byte* getData() const
		{
			return vData;
		}
		byte* getWData()
		{
			return vData;
		}
		void write(const byte *Buf, dword Len, dword Pos = 0);
		void fill(byte B, dword Len, dword Pos);
		void read(byte *Buf, dword Len, dword Pos = 0) const;
		byte& operator [](dword Index)
		{
			if(Index > vSize)
				throw EXC_BadMem;
			return vData[Index];
		}

		byte operator [](dword Index) const
		{
			if(Index > vSize)
				throw EXC_BadMem;
			return vData[Index];
		}
	};

	// packet abstraction
	class cPacket
	{
	private:
		cPacketData *vPData;
		void release();
		void copyifusage()
		{
			if(vPData->usage() > 1)
			{
				cPacketData *N = new cPacketData(vPData->getRealSize());
				N->setLength(vPData->getLength());
				N->write(vPData->getData(), vPData->getLength(), 0);
				release();
				vPData = N;
				vPData->lock();
			}
		}

	public:
		cPacket();
		cPacket(const cPacket &P);
		cPacket(dword Size);
		cPacket(const byte *Buf, dword Len);
		cPacket(byte *Buf, dword Len, bool Own);
		~cPacket();
		cPacket& operator=(const cPacket &P);

		bool operator==(const cPacket &P)
		{
			return (*(P.vPData) == *vPData);
		}
		bool operator!=(const cPacket &P)
		{
			return !(*this == P);
		}
		void clear();
		void insertHeader(const byte *Buf, dword Len);
		void insertTail(const byte *Buf, dword Len);

		void insertHeader(const cPacket &P)
		{
			insertHeader(P.getData(), P.getLength());
		}
		void insertTail(const cPacket &P)
		{
			insertTail(P.getData(), P.getLength());
		}
		void removeHeader(dword Len);
		void removeTail(dword Len);
		dword setRealSize(dword nSize);
		dword getRealSize() const
		{
			return vPData->getRealSize();
		}
		dword setLength(dword nLen);
		dword getLength() const
		{
			return vPData->getLength();
		}
		const byte* getData() const
		{
			return vPData->getData();
		}
		byte* getWData();
		void write(const byte *Buf, dword Len, dword Pos = 0);
		void write(const cPacket &P, dword Pos = 0)
		{
			write(P.getData(), P.getLength(), Pos);
		}
		void fill(byte B, dword Len, dword Pos = 0)
		{
			vPData->fill(B, Len, Pos);
		}

		void read(byte *Buf, dword Len, dword Pos = 0) const
		{
			vPData->read(Buf, Len, Pos);
		}
		//byte& operator [](dword Index);
		byte operator [](dword Index) const
		{
			return (*vPData)[Index];
		}
	};

#ifdef _BAM_DEBUG	
	void DBG_PrintPacket(const cPacket &P);
#endif
}
#endif
